#ifndef DATASTORAGE_HPP_
#define DATASTORAGE_HPP_

#include <stddef.h>
#include <atomic>
#include <mutex>
#include "memstorage_exceptions.hpp"

#define LOGGING_INFO
#include "logger.h"

using std::atomic;
using std::mutex;
using std::lock_guard;

class DataChunk;
class DataStorage {
	friend class DataChunk;

	public:
	//virtual ~DataStorage() = 0; // Base class MUST have (empty) virtual destructor!
	virtual DataChunk* alloc(size_t) = 0;
	DataStorage() = default;
	
	private:
	virtual void free(DataChunk*) = 0;
	virtual char* get(DataChunk*) = 0;
	virtual void release(DataChunk*) = 0;
};

class DataChunk {
	friend class DataStorage;
	friend class MemStorage;
	friend class FileStorage;
	
	public:
	DataChunk(DataStorage* storage, size_t size) 
		: data_(nullptr), storage_(storage), size_(size), locked_(0), touched_(0) {}
	
	inline char* get()
	{
		char* res = data_ ? data_ : data_ = storage_->get(this); 
		//++locked after code with possible exceptions
		++locked_;
		++touched_;
		return data_ = res;
	}
	
	inline void release()
	{
		if (!locked_) {
			LOG_ERROR("wrong get/release balance: <%d> <%p>", locked_, this);
			throw MemAllocException(MemAllocException::E_ALREADY_RELEASED);
		}
		storage_->release(this);
		--locked_;
		++touched_;
	}
	
	inline void free()
	{
		if (locked_) {
			LOG_ERROR("try to free active mem <%p>", this);
			throw MemAllocException(MemAllocException::E_ACTIVE_MEMORY_FREE);
		}
		storage_->free(this);
	}
	
	private:
	char* data_;
	DataStorage* storage_;
	size_t size_;
	int locked_;
	size_t touched_;
	
};

#endif //DATASTORAGE_HPP_
